//
// Created by tao on 2023/7/5.
//

#ifndef LAB_THREAD_POOL_TEST_H
#define LAB_THREAD_POOL_TEST_H

#include <vector>
#include <future>


using namespace std;

class thread_pool_test {
public:
    thread_pool_test(size_t threads);

    ~thread_pool_test();

    //添加任务

    template<class F, class ... Args>
    auto enqueue(F &&f, Args &&... args) -> std::function<typename std::result_of<F(
            Args...)>::type>;//->为返回值类型

private:
    std::vector<std::thread> works;
    std::queue<std::function<void()>> tasks;
    std::mutex queue_mutex;
    std::condition_variable condition;
    bool stop;

};

inline thread_pool_test::thread_pool_test(size_t threads) : stop(false) {
    for (size_t i = 0; i < threads; ++i) {
        works.emplace_back([this] {
            for (;;) {
                std::function<void()> task;
                {
                    std::unique_lock<std::mutex> lock(this->queue_mutex);
                    this->condition.wait(lock,
                                         [this] {
                                             return this->stop || !this->tasks.empty();
                                         });
                    if (this->stop && this->tasks.empty()) {
                        return;
                    }
                    task = std::move(this->tasks.front());
                    this->tasks.pop();
                }
                task();

            }
        });
    }

}

inline thread_pool_test::~thread_pool_test() {
    {
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            stop = true;
        }
        condition.notify_all();
        for (std::thread &worker:works) {
            worker.join();
        }


    }
}

template<class F, class... Args>
auto
thread_pool_test::enqueue(F &&f, Args &&...args) -> std::function<typename std::result_of<F(
        Args...)>::type> {
    using return_type = typename std::result_of<F(Args...)>::type;
    auto task = std::make_shared<std::packaged_task<return_type()>>(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
    );
    std::future<return_type> res = task->get_future();
    {
        std::unique_lock<std::mutex> lock(queue_mutex);
        if (stop) {
            throw std::runtime_error("enqueue on stopped ThreadPool");
        }
        tasks.emplace([task]() { (*task)(); });
    }
    condition.notify_one();
    return res;

}


#endif //LAB_THREAD_POOL_TEST_H
